﻿using NLog;
using ProcDaemon.Configuration;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Configuration;

namespace ProcDaemon
{
    public class Daemon
    {
        private readonly Logger _logger = LogManager.GetCurrentClassLogger();
        private List<ProcessInfo> _processInfos = new List<ProcessInfo>();

        public void Init()
        {
            ReadProcessConfig();
            WriteConsoleHeader();
            WriteProcessInfo();
        }

        public void Run()
        {
            foreach (var item in _processInfos)
            {
                try
                {
                    if (!item.CanReScan())
                    {
                        continue;
                    }

                    var process = Process.GetProcessesByName(item.ProcessName).FirstOrDefault();
                    if (process == null)
                    {
                        ConsoleManager.ClearAndSetCursorPosition(item.CursorLeft, item.CursorTop);
                        ConsoleManager.Write("进程未启动");
                        item.IsFirstRunPerformance = true;
                        if (item.AutoStart)
                        {
                            ConsoleManager.Write(", 即将启动");
                            StartProcess(item);
                            Mail.SendMail(string.Format("进程 {0} 启动成功", item.ProcessName));
                        }
                        continue;
                    }

                    var cpuPercentage = item.PerformanceCounter.NextValue() / Environment.ProcessorCount;
                    // 第一次运行 PerformanceCounter.NextValue() 为0,不需要显示
                    if (item.IsFirstRunPerformance)
                    {
                        ConsoleManager.ClearAndSetCursorPosition(item.CursorLeft, item.CursorTop);
                        ConsoleManager.Write("{0}", "-");
                        item.IsFirstRunPerformance = false;
                        return;
                    }

                    ConsoleManager.ClearAndSetCursorPosition(item.CursorLeft, item.CursorTop);
                    ConsoleManager.Write(string.Format("{0:0.00}%", cpuPercentage));

                    if (cpuPercentage > item.CpuPercentageThreshold)
                    {
                        if (!item.OvertThresholdBeginTime.HasValue)
                        {
                            item.OvertThresholdBeginTime = DateTime.Now;
                        }
                    }
                    else
                    {
                        item.OvertThresholdBeginTime = null;
                    }

                    if (item.OvertThresholdBeginTime.HasValue)
                    {
                        double timeDiff = (DateTime.Now - item.OvertThresholdBeginTime.Value).TotalSeconds;
                        ConsoleManager.Write(string.Format(" 已超CPU阈值 {0:0.##} 秒", timeDiff));

                        if (timeDiff > item.MailSecondsThreshold && !item.ThisTimeHasMailed)
                        {
                            item.ThisTimeHasMailed = true;
                            StringBuilder builder = new StringBuilder();
                            builder.AppendLine(string.Format("进程 {0} 已超CPU阈值 {1:0.##} 秒", item.ProcessName, timeDiff));
                            builder.AppendLine();
                            builder.AppendLine("定义信息:");
                            builder.AppendLine(item.ToString());
                            if (Mail.SendMail(builder.ToString()))
                            {
                                ConsoleManager.Write(" 邮件已发送");
                            }
                        }
                        else
                        {
                            item.ThisTimeHasMailed = false;
                        }

                        if (timeDiff > item.SecondsThreshold)
                        {
                            string msg = string.Format("进程 {0} 已超CPU阈值({1:0.##}%) {2:0.##} 秒, 即将结束进程", item.ProcessName, item.CpuPercentageThreshold, item.SecondsThreshold);
                            _logger.Info(msg);

                            StringBuilder builder = new StringBuilder();
                            builder.AppendLine(msg);
                            builder.AppendLine();
                            builder.AppendLine("==[定义信息]==");
                            builder.AppendLine(item.ToString());
                            if (Mail.SendMail(builder.ToString()))
                            {
                                ConsoleManager.Write(" 邮件已发送");
                            }

                            //Task.Factory.StartNew(() =>
                            //{
                            process.Kill();
                            while (!process.HasExited)
                            {
                                Thread.Sleep(10);
                            }

                            _logger.Info("进程 {0} 已结束", item.ProcessName);

                            StartProcess(item);
                            //});
                        }
                    }
                }
                catch (Exception e)
                {
                    item.IsException = true;
                    ConsoleManager.Write(string.Format("异常, 详见日志, {0}秒后重新检查", item.ReScanSeconds));
                    _logger.Error("进程 {0} 异常: {1}", item.ProcessName, e.ToString());
                }
            }
        }

        private void StartProcess(ProcessInfo item)
        {
            try
            {
                ProcessStartInfo startInfo = new ProcessStartInfo(item.Path, item.Arguments);
                Process.Start(startInfo);
                item.IsFirstRunPerformance = true;
                item.OvertThresholdBeginTime = null;
                item.ThisTimeHasMailed = false;

                _logger.Info("进程 {0} 启动成功", item.ProcessName);
            }
            catch (Exception e)
            {
                item.IsException = true;
                ConsoleManager.ClearAndSetCursorPosition(item.CursorLeft, item.CursorTop);
                ConsoleManager.Write(string.Format("启动失败, 详见日志, {0}秒后重新检查", item.ReScanSeconds));
                _logger.Error("进程 {0} 启动失败: {1}", item.ProcessName, e.ToString());
            }

        }

        private void ReadProcessConfig()
        {
            var settings = ConfigurationManager.GetSection("processSettings") as ProcessSettingsSection;
            foreach (ProcessSection item in settings.processes)
            {
                _processInfos.Add(new ProcessInfo(item.path, item.arguments, item.autoStart, item.cpuPercentageThreshold, item.mailSecondsThreshold, item.restarSecondsThreshold));
            }
        }

        private void WriteConsoleHeader()
        {
            ConsoleManager.WriteLine("{0}{1}{2}{3}{4}", "进程名称", "是否自启动", "CPU阈值(%)", "时间阈值(秒)", "CPU占用率");
            ConsoleManager.WriteLine("-", ConsoleManager.PerColumnWidth * ConsoleManager.TotalColumns - 1, '-');
        }

        private void WriteProcessInfo()
        {
            foreach (var item in _processInfos)
            {
                ConsoleManager.Write("{0}{1}{2}{3}",
                    item.ProcessName,
                    item.AutoStart ? "是" : "否",
                    item.CpuPercentageThreshold.ToString("0.##"),
                    item.SecondsThreshold.ToString("0.##"));

                item.CursorLeft = ConsoleManager.CursorLeft;
                item.CursorTop = ConsoleManager.CursorTop;

                ConsoleManager.Write("{0}", "-");
                ConsoleManager.WriteLine();
            }
        }
    }
}
